package com.koi.resultex.handler;

import cn.dev33.satoken.exception.*;
import com.koi.common.enums.common.IResultEnum;
import com.koi.common.ex.BussinessException;
import com.koi.common.ex.RateLimiterException;
import com.koi.common.ex.RepeatSubmitException;
import com.koi.common.ex.koi.ParamsException;
import com.koi.common.resultex.ErrorHandler;
import com.koi.common.utils.json.JsonUtil;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.support.DefaultMessageSourceResolvable;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.validation.BindException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import org.yaml.snakeyaml.constructor.DuplicateKeyException;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * @author ･ᴗ･
 * @description [全局异常拦截-所有异常必须放置这个类中]
 */
@Slf4j
@Order(Ordered.HIGHEST_PRECEDENCE)
@RestControllerAdvice
public class GlobalExceptionHandler {

    /**
     * @description [拦截所有程序异常]
     * @author ･ᴗ･
     */
    @ExceptionHandler(value = Exception.class)
    public ErrorHandler errorHandler(HttpServletRequest request, Exception ex) {
        log.error("KOI-ADMIN未知异常：{}，请求地址：{}", ex, request.getRequestURL().toString());
        ex.printStackTrace();
        return ErrorHandler.error(500, "未知异常", request.getRequestURL().toString());
    }

    /**
     * @description [自定义异常]
     * @author ･ᴗ･
     */
    @ExceptionHandler(value = ParamsException.class)
    public ErrorHandler errorHandlerParamsException(HttpServletRequest request, ParamsException ex) {
        log.error("KOI-ADMIN自定义异常ParamsException：{}，请求地址：{}", ex.getMessage(), request.getRequestURL().toString());
        ex.printStackTrace();
        return ErrorHandler.error(ex.getCode(), ex.getMessage(), request.getRequestURL().toString());
    }

    /**
     * @description [自定义业务异常]
     * @author ･ᴗ･
     */
    @ExceptionHandler(value = BussinessException.class)
    public ErrorHandler errorHandlerBusinessEx(HttpServletRequest request, BussinessException ex) {
        log.error("业务异常：{}，请求地址：{}", ex.getMsg(), request.getRequestURL().toString());
        ex.printStackTrace();
        return ErrorHandler.error(ex.getStatus(), ex.getMsg(), request.getRequestURL().toString());
    }


    @ExceptionHandler(NotLoginException.class)
    public ErrorHandler handlerNotLoginException(HttpServletRequest request, NotLoginException ex) {
        // 不同异常返回不同状态码
        String message = "";
        if (ex.getType().equals(NotLoginException.NOT_TOKEN)) {
            message = "未提供Token";
        } else if (ex.getType().equals(NotLoginException.INVALID_TOKEN)) {
            message = "未提供有效的Token";
        } else if (ex.getType().equals(NotLoginException.TOKEN_TIMEOUT)) {
            message = "登录信息已过期，请重新登录";
        } else if (ex.getType().equals(NotLoginException.BE_REPLACED)) {
            message = "您的账户已在另一台设备上登录，如非本人操作，请立即修改密码";
        } else if (ex.getType().equals(NotLoginException.KICK_OUT)) {
            message = "已被系统强制下线";
        } else {
            message = "当前会话未登录";
        }
        log.error("Sa-Token异常提示：{}", ex.getMessage());
        // 返回给前端
        return ErrorHandler.error(401, message, request.getRequestURL().toString());
    }

    @ExceptionHandler
    public ErrorHandler handlerNotRoleException(HttpServletRequest request, NotRoleException ex) {
        log.error("Sa-Token提示无此角色：{}", ex.getRole());
        return ErrorHandler.error(401, "无此角色：" + ex.getRole(), request.getRequestURL().toString());
    }

    @ExceptionHandler
    public ErrorHandler handlerNotPermissionException(HttpServletRequest request, NotPermissionException ex) {
        log.error("Sa-Token提示无此权限：{}", ex.getPermission());
        return ErrorHandler.error(401, "无此权限：" + ex.getPermission(), request.getRequestURL().toString());
    }

    @ExceptionHandler
    public ErrorHandler handlerDisableLoginException(HttpServletRequest request, DisableServiceException ex) {
        log.error("Sa-Token提示账户被封禁：{}秒后解封", ex.getDisableTime());
        return ErrorHandler.error(401, "账户被封禁：" + ex.getDisableTime() + "秒后解封", request.getRequestURL().toString());
    }

    @ExceptionHandler
    public ErrorHandler handlerNotSafeException(HttpServletRequest request, NotSafeException ex) {
        log.error("Sa-Token提示二级认证异常：{}", ex.getMessage());
        return ErrorHandler.error(401, "二级认证异常：" + ex.getMessage(), request.getRequestURL().toString());
    }

    @ExceptionHandler
    public ErrorHandler handlerStopMatchException(HttpServletRequest request, StopMatchException ex) {
        log.error("Sa-Token提示路由匹配异常：{}", ex.getMessage());
        return ErrorHandler.error(500, "路由匹配异常", request.getRequestURL().toString());
    }

    @ExceptionHandler
    public ErrorHandler handlerBackResultException(HttpServletRequest request, BackResultException ex) {
        log.error("Sa-Token提示停止匹配：{}", ex.getMessage());
        return ErrorHandler.error(500, "停止匹配", request.getRequestURL().toString());
    }


    /**
     * 对验证的统一异常进行统一处理
     * BindException
     */
    @ExceptionHandler(BindException.class)
    public ErrorHandler handleBindException(BindException e) {
        log.error("自定义验证异常BindException：{}", e.getMessage());
        String message = e.getAllErrors().stream()
                .map(DefaultMessageSourceResolvable::getDefaultMessage)
                .collect(Collectors.joining(", "));
        return ErrorHandler.error(IResultEnum.SERVER_ERROR, JsonUtil.toJsonString(message));
    }

    /**
     * 自定义验证异常
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ErrorHandler handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
        log.error("自定义验证异常MethodArgumentNotValidException：{}", e.getMessage());
        // e.printStackTrace();
        String message = Objects.requireNonNull(e.getBindingResult().getFieldError()).getDefaultMessage();
        return ErrorHandler.error(IResultEnum.SERVER_ERROR, JsonUtil.toJsonString(message));
    }

    /**
     * 对验证异常进行统一处理,List集合方式
     *
     * @param fieldErrorList
     * @return
     */
//    private List<Map<String, String>> toValidatorMsg(List<FieldError> fieldErrorList) {
//        List<Map<String, String>> mapList = new ArrayList<>();
//        for (FieldError fieldError : fieldErrorList) {
//            Map<String, String> map = new HashMap<>();
//            map.put("field", fieldError.getField());
//            map.put("msg", fieldError.getDefaultMessage());
//            mapList.add(map);
//        }
//        return mapList;
//    }

    /**
     * @description [IllegalArgumentException]
     * @author ･ᴗ･
     */
    @ExceptionHandler(IllegalArgumentException.class)
    public ErrorHandler handlerIllegalArgumentException(HttpServletRequest request, IllegalArgumentException ex) {
        log.error("IllegalArgumentException异常：{}，请求地址：{}", ex, request.getRequestURL().toString());
        ex.printStackTrace();
        return ErrorHandler.error(500, ex.getMessage(), request.getRequestURL().toString());
    }

    /**
     * @description [lua限流异常]
     * @author ･ᴗ･
     */
    @ExceptionHandler(RateLimiterException.class)
    public ErrorHandler RateLimitException(HttpServletRequest request, RateLimiterException ex) {
        log.error("lua限流异常：{}，请求地址：{}", ex, request.getRequestURL().toString());
        ex.printStackTrace();
        return ErrorHandler.error(500, ex.getMessage(), request.getRequestURL().toString());
    }

    /**
     * @description [重复提交异常]
     * @author ･ᴗ･
     */
    @ExceptionHandler(RepeatSubmitException.class)
    public ErrorHandler RepeatSubmitException(HttpServletRequest request, RepeatSubmitException ex) {
        log.error("重复提交异常：{}，请求地址：{}", ex, request.getRequestURL().toString());
        ex.printStackTrace();
        return ErrorHandler.error(500, ex.getMessage(), request.getRequestURL().toString());
    }

    /**
     * @description [处理空指针异常]
     * @author ･ᴗ･
     */
    @ExceptionHandler(NullPointerException.class)
    public ErrorHandler exceptionHandler(HttpServletRequest request, NullPointerException ex) {
        log.error("空指针异常，请联系管理员核实：{}，请求地址：{}", ex, request.getRequestURL().toString());
        ex.printStackTrace();
        return ErrorHandler.error(500, "空指针异常", request.getRequestURL().toString());
    }

    /**
     * @description [DB主键冲突异常]
     * @author ･ᴗ･
     */
    @ExceptionHandler(DuplicateKeyException.class)
    public ErrorHandler handleDuplicateKeyException(HttpServletRequest request, DuplicateKeyException ex) {
        log.error("数据库主键冲突异常，请联系管理员核实：{}，请求地址：{}", ex, request.getRequestURL().toString());
        ex.printStackTrace();
        return ErrorHandler.error(500, ex.getMessage(), request.getRequestURL().toString());
    }

    /**
     * @description [运算异常]
     * @author ･ᴗ･
     */
    @ExceptionHandler(ArithmeticException.class)
    public ErrorHandler arithmeticExceptionHandler(HttpServletRequest request, ArithmeticException ex) {
        log.error("运算异常，请联系管理员核实：{}，请求地址：{}", ex, request.getRequestURL().toString());
        ex.printStackTrace();
        return ErrorHandler.error(500, ex.getMessage(), request.getRequestURL().toString());
    }

    /**
     * @description [类型转换异常]
     * @author ･ᴗ･
     */
    @ExceptionHandler(ClassCastException.class)
    public ErrorHandler classCastExceptionHandler(HttpServletRequest request, ClassCastException ex) {
        log.error("类型转换异常，请联系管理员核实：{}，请求地址：{}", ex, request.getRequestURL().toString());
        ex.printStackTrace();
        return ErrorHandler.error(500, ex.getMessage(), request.getRequestURL().toString());
    }

    /**
     * @description [数据下标越界异常]
     * @author ･ᴗ･
     */
    @ExceptionHandler(IndexOutOfBoundsException.class)
    public ErrorHandler indexOutOfBoundsExceptionHandler(HttpServletRequest request, IndexOutOfBoundsException ex) {
        log.error("数据下标越界异常，请联系管理员核实：{}，请求地址：{}", ex, request.getRequestURL().toString());
        ex.printStackTrace();
        return ErrorHandler.error(500, ex.getMessage(), request.getRequestURL().toString());
    }

    /**
     * @description [文件未找到异常]
     * @author ･ᴗ･
     */
    @ExceptionHandler(FileNotFoundException.class)
    public ErrorHandler fileNotFoundExceptionHandler(HttpServletRequest request, FileNotFoundException ex) {
        log.error("文件未找到异常，请联系管理员核实：{}，请求地址：{}", ex, request.getRequestURL().toString());
        ex.printStackTrace();
        return ErrorHandler.error(500, ex.getMessage(), request.getRequestURL().toString());
    }

    /**
     * @description [IO异常]
     * @author ･ᴗ･
     */
    @ExceptionHandler(IOException.class)
    public ErrorHandler IOExceptionHandler(HttpServletRequest request, IOException ex) {
        log.error("IO异常，请联系管理员核实：{}，请求地址：{}", ex, request.getRequestURL().toString());
        ex.printStackTrace();
        return ErrorHandler.error(500, ex.getMessage(), request.getRequestURL().toString());
    }

    /**
     * @description [参数类型不匹配]
     * @author ･ᴗ･
     */
    @ExceptionHandler({MethodArgumentTypeMismatchException.class})
    public ErrorHandler requestTypeMismatch(HttpServletRequest request, MethodArgumentTypeMismatchException ex) {
        log.error("参数类型不匹配异常,请联系管理员核实：{}，请求地址：{}", ex, request.getRequestURL().toString());
        ex.printStackTrace();
        return ErrorHandler.error(500, ex.getMessage(), request.getRequestURL().toString());
    }

}
